#include "Resources.h"
#include "SoundManager.h"
#include "Sound.h"
#include "SoundFactory.h"
#include "Structure.h"
#include "StaticSound.h"
#include "StaticWaveSound.h"
#include "StreamedSound.h"
#include "StreamedWaveSound.h"

#include "OpenAL/al.h"
#include "OpenAL/alc.h"
#include "OpenAL/alut.h"
#include "OpenAL/xram.h"
#include "OpenAL/efx.h"
#include "OpenAL/EFX-Util.h"

#include <string>
#include <map>
#include <vector>
#include <deque>
#include <iterator>

#include <boost/thread/thread.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/xtime.hpp>


#pragma once

boost::thread *UniverseSound::SoundManager::mUpdateThread = 0;
bool UniverseSound::SoundManager::mShutDown = false;

template<> UniverseSound::SoundManager *Ogre::Singleton<UniverseSound::SoundManager>::ms_Singleton = 0;

namespace UniverseSound
{
	using namespace Ogre;

	SoundManager::SoundManager() :
		mNumSources(0),
		mDevice(0),
		mContext(0),
		mListener(0),
		mEAXSupport(false),
		mEFXSupport(false),
		mXRAMSupport(false),
		mXRAMSize(0),
		mXRAMFree(0),
		mXRAMAuto(0),
		mXRAMHardware(0),
		mXRAMAccessible(0),
		mCurrentXRAMMode(0),
		mEAXVersion(0),
		mFilterList(0),
		mEffectList(0),
		mEffectSlotList(0),
		mDeviceStrings(0)
		{
			// Effect objects
			alGenEffects = NULL;
			alDeleteEffects = NULL;
			alIsEffect = NULL;
			alEffecti = NULL;
			alEffectiv = NULL;
			alEffectf = NULL;
			alEffectfv = NULL;
			alGetEffecti = NULL;
			alGetEffectiv = NULL;
			alGetEffectf = NULL;
			alGetEffectfv = NULL;

			//Filter objects
			alGenFilters = NULL;
			alDeleteFilters = NULL;
			alIsFilter = NULL;
			alFilteri = NULL;
			alFilteriv = NULL;
			alFilterf = NULL;
			alFilterfv = NULL;
			alGetFilteri = NULL;
			alGetFilteriv = NULL;
			alGetFilterf = NULL;
			alGetFilterfv = NULL;

			// Auxiliary slot object
			alGenAuxiliaryEffectSlots = NULL;
			alDeleteAuxiliaryEffectSlots = NULL;
			alIsAuxiliaryEffectSlot = NULL;
			alAuxiliaryEffectSloti = NULL;
			alAuxiliaryEffectSlotiv = NULL;
			alAuxiliaryEffectSlotf = NULL;
			alAuxiliaryEffectSlotfv = NULL;
			alGetAuxiliaryEffectSloti = NULL;
			alGetAuxiliaryEffectSlotiv = NULL;
			alGetAuxiliaryEffectSlotf = NULL;
			alGetAuxiliaryEffectSlotfv = NULL;

			mNumEffectSlots = 0;
			mNumSendsPerSource = 0;
		}

		SoundManager::~SoundManager()
		{
			mShutDown = true;
			if (mUpdateThread)
			{
				mUpdateThread->join();
				delete mUpdateThread;
				mUpdateThread = 0;
			}

			_release();
			alcMakeContextCurrent(0);
			alcDestroyContext(mContext);
			alcCloseDevice(mDevice);

			delete mListener;
		}

		SoundManager *SoundManager::getSingletonPtr()
		{
			return ms_Singleton;
		}

		SoundManager& SoundManager::getSingleton()
		{
			assert (ms_Singleton);
			return (*ms_Singleton);
		}

		bool SoundManager::initialize(const std::string& device)
		{
			Ogre::LogManager::getSingleton().logMessage("UniverseSound: Initializing sound engine!", LML_NORMAL, false);
			
			// List all available audio devices
			_listAudioDevices();
			
			// First we detect the version of ALC to print it in the log file
			int majorVersion;
			int minorVersion;

			// Get the ALC major version...
			alcGetIntegerv(NULL, ALC_MAJOR_VERSION, sizeof(majorVersion), &majorVersion);
			// ... or print a warning
			if (alGetError())
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to get OpenAL version number!", LML_NORMAL);
			}
			// Get the ALC minor version
			alcGetIntegerv(NULL, ALC_MINOR_VERSION, sizeof(minorVersion), &minorVersion);
			// ... or print a warning
			if (alGetError())
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to get OpenAL version number!", LML_NORMAL);
			}
			// Create a log message containing the major and minor version number ...
			Ogre::String& msg = "UniverseSound: OpenAL version" + Ogre::StringConverter::toString(majorVersion) +"." + Ogre::StringConverter::toString(minorVersion) +" detected";
			// ... and print the message in the log file
			Ogre::LogManager::getSingleton().logMessage(msg);

			// Now we want to print a device list
			// OpenAL versions prior to OpenAL 1.1 DO NOT support device listing, so we have to make sure that
			// the version on the target computer has version 1.1
			bool deviceInList = false;
			if (majorVersion >= 1 && minorVersion >= 1)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: List of available devices:", LML_NORMAL);

				Ogre::StringVector deviceList = getDeviceList();
				std::stringstream ss;
				Ogre::StringVector::iterator deviceItr;
				for (deviceItr = deviceList.begin(); deviceItr != deviceList.end() && (*deviceItr).compare("") != 0; deviceItr++)
				{
					deviceInList |= (*deviceItr).compare(device) == 0;
					ss << (*deviceItr);
					Ogre::LogManager::getSingleton().logMessage(ss.str());
					ss.clear();
					ss.str("");
				}
			}

			mDevice = alcOpenDevice(deviceInList ? device.c_str() : NULL);
			if (!mDevice)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to create audio device", LML_CRITICAL);
			}
			if (!deviceInList)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Choosing " + Ogre::String(alcGetString(mDevice, ALC_DEVICE_SPECIFIER))+" as default audio device", LML_NORMAL);
			}
			else
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Choosing " + Ogre::String(alcGetString(mDevice, ALC_DEVICE_SPECIFIER)), LML_NORMAL);
			}
			Ogre::LogManager::getSingleton().logMessage("UniverseSound: OpenAL device successfully created", LML_NORMAL);
			
			ALint attribs[2] = {ALC_MAX_AUXILIARY_SENDS, 4};

			mContext = alcCreateContext(mDevice, attribs);
			if (!mContext)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to create OpenAL context", LML_CRITICAL);
			}
			else
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: OpenAL context successfully created", LML_NORMAL);
			}

			if (!alcMakeContextCurrent(mContext))
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to set current OpenAL context", LML_CRITICAL);
			}

			_checkFeatureSupport();

			mListener = new Listener();

			mNumSources = createSourcePool();

			msg = "UniverseSound: " + Ogre::StringConverter::toString(mNumSources) + " sources for simultaneous sound playback created";
			Ogre::LogManager::getSingleton().logMessage(msg);

			mUpdateThread = new boost::thread(boost::function0<void>(&SoundManager::threadUpdate));
			
			Ogre::LogManager::getSingleton().logMessage("UniverseSound: Sound engine successfully initialized !", LML_NORMAL);

			return true;
		}

		Ogre::StringVector SoundManager::getDeviceList()
		{
			const ALCchar* deviceList = alcGetString(NULL, ALC_DEVICE_SPECIFIER);

			Ogre::StringVector deviceVector;
			while(*deviceList != NULL)
			{
				try
				{
					ALCdevice *device = alcOpenDevice(deviceList);
					if (alcGetError(device))
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to open the selected device", LML_CRITICAL);
					}
					if (device)
					{
						// Device seems to be valid
						ALCcontext *context = alcCreateContext(device, NULL);
						if (alcGetError(device)) 
						{
							Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to create context", LML_CRITICAL);
						}
						if(context)
						{
							// Context seems to be valid
							alcMakeContextCurrent(context);
							if(alcGetError(device)) 
								Ogre::LogManager::getSingleton().logMessage("UniverSound: Unable to make context current", LML_CRITICAL);
							deviceVector.push_back(alcGetString(device, ALC_DEVICE_SPECIFIER));
							alcMakeContextCurrent(NULL);
							if(alcGetError(device))
								Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to clear current context", LML_CRITICAL);
							alcDestroyContext(context);
							if(alcGetError(device)) 
								Ogre::LogManager::getSingleton().logMessage("Unable to destroy current context", LML_CRITICAL);
						}
					alcCloseDevice(device);
				}
			}
			catch(...)
			{
				// Don't die here, we'll just skip this device.
			}

			deviceList += strlen(deviceList) + 1;
		}

		return deviceVector;
		}

		Ogre::StringVector SoundManager::getSoundList()
		{
			Ogre::StringVector soundList;
			for (SoundMap::iterator iter = mSoundMap.begin(); iter != mSoundMap.end(); ++iter)
				soundList.push_back((*iter).first);
			return soundList;
		}

		void SoundManager::_checkFeatureSupport()
		{
			mEFXSupport = _checkEFXSupport();
			if (mEFXSupport)
				_determineAuxEffectSlots();

			mXRAMSupport = _checkXRAMSupport();
			
			for (int version = 5; version >= 2; version--)
			{
				Ogre::String eaxName="EAX" + Ogre::StringConverter::toString(version) + ".0";
				if (alIsExtensionPresent(eaxName.c_str()) == AL_TRUE)
				{
					mEAXSupport = true;
					mEAXVersion = version;
					break;
				}
			}
		}

		void SoundManager::_listAudioDevices()
		{
			mDeviceStrings = const_cast<ALCchar*>(alcGetString(0,ALC_DEVICE_SPECIFIER));
		}

		SoundClip *SoundManager::createCustomSound(const std::string& name, const std::string& file, bool stream, bool loop, bool prebuffer)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);
			Ogre::ResourceGroupManager* groupManager = 0;
			Ogre::String group;
			Ogre::DataStreamPtr soundData;
			ALuint buffer = AL_NONE;
			// If sound is static...
			if (!stream)
			{
				// ... get shared buffer if available
				if ((buffer = SoundManager::getSingleton()._getSharedBuffer(file)) == AL_NONE)
				{
					// Shared buffer is not available - try finding resource in OGRE resources
					groupManager = Ogre::ResourceGroupManager::getSingletonPtr();
					try
					{
						// Find resource file
						group = groupManager->findGroupContainingResource(file);
						// Open resource file
						soundData = groupManager->openResource(file, group);
					}
					catch (...)
					{
						// Sound file not available
						Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to find sound file! Have you specified a resource location?", LML_CRITICAL);
						return (0);
					}
				}
			}
			// Streamed sound
			else
			{
				// Get Ogre's resource group manager
				groupManager = Ogre::ResourceGroupManager::getSingletonPtr();

				try
				{
					// Find the resource file
					group = groupManager->findGroupContainingResource(file);
					// Open the resource file
					soundData = groupManager->openResource(file, group);
				}
				catch (...)
				{
					// Sound file is not available
					Ogre::LogManager::getSingleton().logMessage("UniverseSound: Unable to find sound file! Have you specified a resource location?", LML_CRITICAL);
					return (0);
				}
			}

			// Check if file type is OGG or UAC format
			if (file.find(".ogg") != std::string::npos || file.find(".OGG") != std::string::npos || file.find(".uac") != std::string::npos || file.find(".UAC") != std::string::npos)
			{
				// Check if the filename is unique
				if (mSoundMap.find(name) != mSoundMap.end())
				{
					Ogre::String msg = "UniverseSound: Sound with name: "+name+" already exists!";
					Ogre::LogManager::getSingleton().logMessage(msg);
					return 0;
				}

				// If stream param is true, create stream sound...
				if(stream)
					mSoundMap[name] = new StreamedSound(name);
				// else create static sound
				else
					mSoundMap[name] = new StaticSound(name);

				// Set loop flag if loop param is true
				mSoundMap[name]->loop(loop);
				
				// Open file in threaded mode
				threadedFileOpen *fileOpen = new threadedFileOpen;
				fileOpen->mPrebuffer = prebuffer;
				fileOpen->mBuffer = buffer;
				fileOpen->mFileName = file;
				fileOpen->mFile = soundData;
				fileOpen->mSound = mSoundMap[name];
				mLoadedSounds.push_back(fileOpen);

				return mSoundMap[name];
			}

			// Check if file type is wave format
			else if	(file.find(".wav") != std::string::npos || file.find(".WAV") != std::string::npos)
			{
				// Check if the filename is unique
				if (mSoundMap.find(name) != mSoundMap.end())
				{
					Ogre::String msg="UniverseSound: Sound with name: "+name+" already exists!";
					Ogre::LogManager::getSingleton().logMessage(msg);
					return 0;
				}

				// Create a wave stream sound or wave static sound
				if (stream)
					mSoundMap[name] = new StreamedWaveSound(name);
				else
					mSoundMap[name] = new StaticWaveSound(name);

				// Set loop flag
				mSoundMap[name]->loop(loop);
				threadedFileOpen* fileOpen = new threadedFileOpen;
				fileOpen->mPrebuffer	= prebuffer;		// Prebuffer flag
				fileOpen->mBuffer		= buffer;			// Shared buffer ref
				fileOpen->mFileName		= file;				// Filename to register
				fileOpen->mFile			= soundData;		// DatastreamPtr
				fileOpen->mSound		= mSoundMap[name];	// Sound object
				mLoadedSounds.push_back(fileOpen);

				return mSoundMap[name];
			}
			else
			{
				Ogre::String msg = "UniverseSound: Error while creating a new sound - The sound" + name + " has no supported extension";
				Ogre::LogManager::getSingleton().logMessage(msg, LML_CRITICAL);
				return NULL;
			}
		}

		SoundClip *SoundManager::createCustomSound(Ogre::SceneManager& sceneManager, const std::string& name, const std::string& file, bool stream, bool loop, bool prebuffer)
		{
			//boost::recursive_mutex::scoped_lock 1(mMutex);
			Ogre::NameValuePairList params;
			params["fileName"]	= file;
			params["stream"]	= stream	? "true" : "false";
			params["loop"]		= loop		? "true" : "false";
			params["prebuffer"]	= prebuffer ? "true" : "false";

			SoundClip *sound = 0;

			try
			{
				sound = static_cast<SoundClip*>(sceneManager.createMovableObject(name, SoundFactory::FACTORY_TYPE_NAME, &params));
				sound->mSceneMgr = &sceneManager;
			}
			catch (...)
			{	
				LogManager::getSingleton().logMessage("UniverseSound: Couldn't create sound because the sound plugin did not load properly", LML_CRITICAL);
			}
			// create the movable sound
			return sound;
		}

		SoundClip *SoundManager::getSound(const std::string& name)
		{
			//boost::recursive_mutex::scoped_lock 1(mMutex);

			SoundMap::iterator i = mSoundMap.find(name);
			if (i == mSoundMap.end())
				return 0;

			return i->second;
		}

		bool SoundManager::isEffectSupported(ALint effectID)
		{
			// Find the effect ID in the list of supported effects
			// If the effect was found, return
			if (mEFXSupportList.find(effectID) != mEFXSupportList.end())
				return mEFXSupportList[effectID];
			else
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::isEffectSupported(ALint effectID) - The value 'effectID' is invalid", LML_NORMAL);

			// Otherwise the effect is not supported, return false
			return false;
		}

		void SoundManager::_determineAuxEffectSlots()
		{
			ALuint		uiEffectSlots[128] = {0};
			ALuint		uiEffects[1] = {0};
			ALuint		uiFilters[1] = {0};
			Ogre::String msg="";

			// Create as many auxiliary effect slots as possible (up to 128) until the call is failure
			for (mNumEffectSlots = 0; mNumEffectSlots < 128; mNumEffectSlots++)
			{
				alGenAuxiliaryEffectSlots(1, &uiEffectSlots[mNumEffectSlots]);
				if (alGetError() != AL_NO_ERROR)
					break;
			}
			alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &mNumSendsPerSource);

			alGenEffects(1, &uiEffects[0]);
			if (alGetError() == AL_NO_ERROR)
			{
				// Try all effects
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_REVERB);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_CHORUS);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_DISTORTION);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_ECHO);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_FLANGER);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_FREQUENCY_SHIFTER);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_VOCAL_MORPHER);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_PITCH_SHIFTER);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_RING_MODULATOR);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_AUTOWAH);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_COMPRESSOR);
				alEffecti(uiEffects[0], AL_EFFECT_TYPE, AL_EFFECT_EQUALIZER);
			}

			alGenFilters(1, &uiFilters[0]);
			if (alGetError() == AL_NO_ERROR)
			{
				// Try all filters
				alFilteri(uiFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS);
				alFilteri(uiFilters[0], AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
				alFilteri(uiFilters[0], AL_FILTER_TYPE, AL_FILTER_BANDPASS);
			}

			// Delete all filters
			alDeleteFilters(1, &uiFilters[0]);

			// Delete all effects
			alDeleteEffects(1, &uiEffects[0]);

			// Delete all auxiliary effect slots
			alDeleteAuxiliaryEffectSlots(mNumEffectSlots, uiEffectSlots);
		}

		bool SoundManager::_checkEFXSupport()
		{
			if (alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"))
			{
				// Get function pointers
				alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
				alDeleteEffects = (LPALDELETEEFFECTS )alGetProcAddress("alDeleteEffects");
				alIsEffect = (LPALISEFFECT )alGetProcAddress("alIsEffect");
				alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
				alEffectiv = (LPALEFFECTIV)alGetProcAddress("alEffectiv");
				alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
				alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
				alGetEffecti = (LPALGETEFFECTI)alGetProcAddress("alGetEffecti");
				alGetEffectiv = (LPALGETEFFECTIV)alGetProcAddress("alGetEffectiv");
				alGetEffectf = (LPALGETEFFECTF)alGetProcAddress("alGetEffectf");
				alGetEffectfv = (LPALGETEFFECTFV)alGetProcAddress("alGetEffectfv");
				alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
				alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
				alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
				alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
				alFilteriv = (LPALFILTERIV)alGetProcAddress("alFilteriv");
				alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
				alFilterfv = (LPALFILTERFV)alGetProcAddress("alFilterfv");
				alGetFilteri = (LPALGETFILTERI )alGetProcAddress("alGetFilteri");
				alGetFilteriv= (LPALGETFILTERIV )alGetProcAddress("alGetFilteriv");
				alGetFilterf = (LPALGETFILTERF )alGetProcAddress("alGetFilterf");
				alGetFilterfv= (LPALGETFILTERFV )alGetProcAddress("alGetFilterfv");
				alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
				alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
				alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
				alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
				alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV)alGetProcAddress("alAuxiliaryEffectSlotiv");
				alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
				alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");
				alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI)alGetProcAddress("alGetAuxiliaryEffectSloti");
				alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV)alGetProcAddress("alGetAuxiliaryEffectSlotiv");
				alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF)alGetProcAddress("alGetAuxiliaryEffectSlotf");
				alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV)alGetProcAddress("alGetAuxiliaryEffectSlotfv");

				// If all pointers are valid return true, if this is not the case return false
				if (alGenEffects &&	alDeleteEffects && alIsEffect && alEffecti && alEffectiv &&	alEffectf &&
					alEffectfv && alGetEffecti && alGetEffectiv && alGetEffectf && alGetEffectfv &&	alGenFilters &&
					alDeleteFilters && alIsFilter && alFilteri && alFilteriv &&	alFilterf && alFilterfv &&
					alGetFilteri &&	alGetFilteriv && alGetFilterf && alGetFilterfv && alGenAuxiliaryEffectSlots &&
					alDeleteAuxiliaryEffectSlots &&	alIsAuxiliaryEffectSlot && alAuxiliaryEffectSloti &&
					alAuxiliaryEffectSlotiv && alAuxiliaryEffectSlotf && alAuxiliaryEffectSlotfv &&
					alGetAuxiliaryEffectSloti && alGetAuxiliaryEffectSlotiv && alGetAuxiliaryEffectSlotf &&
					alGetAuxiliaryEffectSlotfv)
				return true;
			}
			return false;
		}

		bool SoundManager::_checkXRAMSupport()
		{
			// Check for X-RAM extension
			if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
			{
				// Get X-RAM Function pointers
				mEAXSetBufferMode = (EAXSetBufferMode)alGetProcAddress("EAXSetBufferMode");
				mEAXGetBufferMode = (EAXGetBufferMode)alGetProcAddress("EAXGetBufferMode");

				if (mEAXSetBufferMode && mEAXGetBufferMode)
				{
					mXRAMSize = alGetEnumValue("AL_EAX_RAM_SIZE");
					mXRAMFree = alGetEnumValue("AL_EAX_RAM_FREE");
					mXRAMAuto = alGetEnumValue("AL_STORAGE_AUTOMATIC");
					mXRAMHardware = alGetEnumValue("AL_STORAGE_HARDWARE");
					mXRAMAccessible = alGetEnumValue("AL_STORAGE_ACCESSIBLE");

					if (mXRAMSize && mXRAMFree && mXRAMAuto && mXRAMHardware && mXRAMAccessible)
					{
						// XRAM Support available
						// Get memory size and free memory size
						// Return true - support is detected
						mXRAMSizeMB = alGetInteger(mXRAMSize) / (1024*1024);
						mXRAMFreeMB = alGetInteger(mXRAMFree) / (1024*1024);
						return true;
					}
				}
			}
			// No support detected
			return false;
		}

		void SoundManager::setXRAMBuffer(ALsizei numBuffers, ALuint *buffer)
		{
			if (buffer && mEAXSetBufferMode)
				mEAXSetBufferMode(numBuffers, buffer, mCurrentXRAMMode);
		}

		void SoundManager::setXRAMBufferMode(ALenum mode)
		{
			mCurrentXRAMMode = mXRAMAuto;
			if (mode == mXRAMAuto) 
				mCurrentXRAMMode = mXRAMAuto;
			else if (mode == mXRAMHardware) 
				mCurrentXRAMMode = mXRAMHardware;
			else if (mode == mXRAMAccessible) 
				mCurrentXRAMMode = mXRAMAccessible;
		}

		bool SoundManager::setEFXEffectParam(const std::string& name, ALint type, ALenum attrib, ALfloat parameter)
		{
			// Is EFX support available, validate name parameter
			if (!isEFXSupported() && name.empty()) 
				return false;

			ALuint effect;

			// Get effect ID's
			if ((effect = _getEFXEffect(name)) != AL_EFFECT_NULL)
			{
				alGetError();
				alEffecti(type, attrib, parameter);
				// If an error comes up write log entry and return false
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXEffectParam() - Unable to change effect parameter !", LML_NORMAL);
					return false;
				}
			}
			return false;
		}

		bool SoundManager::setEFXEffectParam(const std::string& name, ALint type, ALenum attrib, ALfloat *params)
		{
			// Is EFX support available, validate name parameter
			if (!isEFXSupported() && name.empty() || !params) 
				return false;

			ALuint effect;

			// Get effect id's
			if ((effect = _getEFXEffect(name)) != AL_EFFECT_NULL)
			{
				alGetError();
				alEffectfv(type, attrib, params);
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXEffectParam() - Unable to change effect parameters !", LML_NORMAL);
					return false;
				}
			}	
			return false;
		}

		bool SoundManager::setEFXEffectParam(const std::string& name, ALint type, ALenum attrib, ALint parameter)
		{
			if (!isEFXSupported() && name.empty()) 
				return false;

			ALuint effect;

			// Get effect ID's
			if ((effect = _getEFXEffect(name)) != AL_EFFECT_NULL)
			{
				alGetError();
				alEffecti(type, attrib, parameter);
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXEffectParam() - Unable to change effect parameter !", LML_NORMAL);
					return false;
				}
			}
			return false;
		}

		bool SoundManager::setEFXEffectParam(const std::string& name, ALint type, ALenum attrib, ALint *params)
		{
			if (!isEFXSupported() && name.empty() || !params) 
				return false;

			ALuint effect;

			// Get effect ID's
			if ((effect = _getEFXEffect(name)) != AL_EFFECT_NULL)
			{
				alGetError();
				alEffectiv(type, attrib, params);
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXEffectParam() - Unable to change effect parameters !", LML_NORMAL);
					return false;
				}
			}
			return false;
		}

		bool SoundManager::attachEffect(const std::string& soundName, ALuint slotID, const Ogre::String& effectName, const Ogre::String& filterName)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			// Is EFX support available, is soundName param valid
			if (!isEFXSupported() && soundName.empty()) 
				return false;

			ALuint effect;
			ALuint filter;
			ALuint slot;

			// Get effect id's
			slot	= _getEFXEffectSlot(slotID);
			effect	= _getEFXEffect(effectName);
			filter	= _getEFXFilter(filterName);

			// Attach effect and filter to slot
			if (_attachEffectToSlot(slot, effect))
			{
				SoundClip *sound = getSound(soundName);
				if (sound)
				{
					ALuint src = sound->getSource();
					if (src != AL_NONE)
					{
						alSource3i(src, AL_AUXILIARY_SEND_FILTER, effect, slotID, filter);
						if (alGetError() == AL_NO_ERROR)
						{
							return true;
						}
						else
						{
							Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachEffect() - Unable to attach effect to source !", LML_NORMAL);
							return false;
						}
					}
					else
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachEffect() - The specified sound has no source !", LML_NORMAL);
						return false;
					}
				}
				else
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachEffect() - The specified sound has no source !", LML_NORMAL);
					return false;
				}
			}
			return false;
		}

		bool SoundManager::attachFilter(const std::string& soundName, const Ogre::String& filterName)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			// Validate sound name, is EFX support available
			if (!isEFXSupported() && soundName.empty()) 
				return false;

			ALuint filter = _getEFXFilter(filterName);

			if (filter != AL_FILTER_NULL)
			{
				// Get sound name
				SoundClip *sound = getSound(soundName);

				if (sound)
				{
					// Get source
					ALuint src = sound->getSource();
					if (src != AL_NONE )
					{
						// Attach filter
						alSourcei(src, AL_DIRECT_FILTER, filter);
						if (alGetError() == AL_NO_ERROR)
						{
							// If there is no error, return true
							return true;
						}
						else
						{
							// Or log error
							Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachFilter(...) - Unable to attach filter to source!", LML_NORMAL);
							return false;
						}
					}
					else
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachFilter(...) - The sound has no source !", LML_NORMAL);
						return false;
					}
				}
				else
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::attachFilter() - Unable to find requested sound! Spelling correct? Sound created?", LML_NORMAL);
					return false;
				}

			}
			return false;
		}

		bool SoundManager::detachEffect(const std::string& soundName, ALuint slotID)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);
			// Is EFX support available, is sound name param valid
			if (!isEFXSupported() && soundName.empty()) 
				return false;

			ALuint slot;
			// Get slot
			slot = _getEFXEffectSlot(slotID);
			// Detach effect from sound
			// Validate slot
			if (slot != AL_NONE)
			{
				// Get the sound
				SoundClip *sound = getSound(soundName);

				// Validate sound
				if (sound)
				{
					// Get the source
					ALuint src = sound->getSource();
					// Validate the source
					if (src != AL_NONE)
					{
						// Detach effect
						alSource3i(src, AL_AUXILIARY_SEND_FILTER, AL_EFFECT_NULL, slot, AL_FILTER_NULL);
						if (alGetError() == AL_NO_ERROR)
						{
							// Detaching successfull
							return true;
						}
						else
						{
							Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachEffect(...) - Unable to detach effect from source!", LML_NORMAL);
							return false;
						}
					}
					else
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachEffect(...) - The sound has no source !");
						return false;
					}
				}
				else
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachEffect(...) - Unable to find requested sound! Spelling correct? Sound created?", LML_NORMAL);
					return false;
				}
			}
			return false;
		}

		bool SoundManager::detachFilter(const std::string& soundName)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);
			// Validation
			if (!isEFXSupported() && soundName.empty()) 
				return false;

			// Get the sound
			SoundClip *sound = getSound(soundName);

			// Validate sound
			if (sound)
			{
				// Get the sound source
				ALuint src = sound->getSource();
				// Validate source
				if (src != AL_NONE)
				{
					// Detach filter
					alSourcei(src, AL_DIRECT_FILTER, AL_FILTER_NULL);
					if (alGetError() != AL_NO_ERROR)
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachFilter(...) - Unable to detach filter from source!", LML_NORMAL);
						return false;
					}
				}
				else
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachFilter(...) - The sound has no source!", LML_NORMAL);
					return false;
				}
			}
			else
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::detachFilter(...) - Unable to find requested sound! Spelling correct? Sound created?", LML_NORMAL);
				return false;
			}

			return true;
		}

		bool SoundManager::_attachEffectToSlot(ALuint slot, ALuint effect)
		{
			// Validation
			if (!isEFXSupported() || slot == AL_NONE) 
				return false;

			// Attach effect to specified slot
			alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect);
			if (alGetError() != AL_NO_ERROR)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::_attachEffectToSlot(...) - Unable to attach the effect to the slot", LML_NORMAL);
				return false;
			}
			return true;
		}

		ALuint SoundManager::_getEFXFilter(const std::string& name)
		{
			// Validation
			if (!mFilterList || !isEFXSupported() || name.empty()) 
				return AL_FILTER_NULL;

			// Find filter in filter list
			EffectList::iterator filter = mFilterList->find(name);
			if (filter == mFilterList->end())
				return AL_FILTER_NULL;
			else
				return filter->second;
		}

		ALuint SoundManager::_getEFXEffect(const std::string& name)
		{
			// Validation
			if (!mEffectList || !isEFXSupported() || name.empty()) 
				return AL_EFFECT_NULL;

			// Find effect in effect list
			EffectList::iterator effect = mEffectList->find(name);
			if (effect == mEffectList->end())
				return AL_EFFECT_NULL;
			else
				return effect->second;
		}

		ALuint SoundManager::_getEFXEffectSlot(int slotID)
		{
			// Validation
			if (!mEffectSlotList || !isEFXSupported() || (slotID >= static_cast<int>(mEffectSlotList->size()))) 
				return AL_NONE;

			// Return effect slot
			return static_cast<ALuint>((*mEffectSlotList)[slotID]);
		}

		bool SoundManager::createEFXFilter(const std::string& name, ALint type, ALfloat gain, ALfloat hfGain)
		{
			// Validation
			if (!isEFXSupported() || name.empty() || !isEffectSupported(type))
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXfilter(...) - The specified filter is not supported", LML_NORMAL);
				ALuint filter;

				// Generate filter
				alGenFilters(1, &filter);
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXFilter(...) - Unable to create EFX filter", LML_NORMAL);
					return false;
				}

				// Validation
				if (alIsFilter(filter) && ((type == AL_FILTER_LOWPASS) || (type == AL_FILTER_HIGHPASS) || (type==AL_FILTER_BANDPASS)))
				{
					// Specify filter
					alFilteri(filter, AL_FILTER_TYPE, type);
					if (alGetError() != AL_NO_ERROR)
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXFilter(...) - The specified filter is not supported", LML_NORMAL);
						return false;
					}
					else
					{
						// Set properties
						alFilterf(filter, AL_LOWPASS_GAIN, gain);
						alFilterf(filter, AL_LOWPASS_GAINHF, hfGain);

						// Create new filter list
						if (!mFilterList) 
							mFilterList = new EffectList;
						(*mFilterList)[name] = filter;
					}
				}
				return true;
			}
			return false;
		}

		bool SoundManager::createEFXEffect(const std::string& name, ALint type, EAXREVERBPROPERTIES *properties)
		{
			// Validation
			if (!isEFXSupported() || name.empty() || !isEffectSupported(type))
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXEffect(...) - The specified effect is not supported", LML_NORMAL);
				return false;
			}

			ALuint effect;

			alGenEffects(1, &effect);
			if (alGetError() != AL_NO_ERROR)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXEffect(...) - Unable to create EFX effect", LML_NORMAL);
				return false;
			}

			if (alIsEffect(effect))
			{	
				alEffecti(effect, AL_EFFECT_TYPE, type);
				if (alGetError() != AL_NO_ERROR)
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXEffect(...) - The effect is not supported", LML_NORMAL);
					return false;
				}
				else
				{
					// Apply some preset reverb properties
					if (type == AL_EFFECT_EAXREVERB  && properties)
					{
						EFXEAXREVERBPROPERTIES eaxProps;
						ConvertReverbParameters(properties, &eaxProps);
						_setEAXReverbProps(&eaxProps, effect);
					}

					// Add to list
					if (!mEffectList) mEffectList = new EffectList;
					(*mEffectList)[name]=effect;
				}
			}
			return true;
		}

		bool SoundManager::createEFXSlot()
		{
			if (!isEFXSupported())
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXSlot() - No EFX support detected on you system!", LML_NORMAL);
				return false;
			}

			ALuint slot;

			alGenAuxiliaryEffectSlots(1, &slot);
			if (alGetError() != AL_NO_ERROR)
			{
				Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::createEFXSlot() - Unable to create axiliary effect slot", LML_NORMAL);
				return false;
			}
			else
			{
				if (!mEffectSlotList) 
					mEffectSlotList = new std::vector<ALuint>;
				mEffectSlotList->push_back(slot);
			}
			return true;
		}

		int SoundManager::getNumEffectSlotsSupported()
		{
			if (!isEFXSupported())
				return 0;

			ALint auxSends = 0;
			alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &auxSends);

			return auxSends;
		}

		int SoundManager::getNumEffectSlotsCreated()
		{
			// Validation
			if (!mEffectSlotList) 
				return 0;

			// Return number of effect slots in effect slot list
			return static_cast<int>(mEffectSlotList->size());
		}

		bool SoundManager::_setEAXReverbProps(EFXEAXREVERBPROPERTIES *properties, ALuint uiEffect)
		{
			if (properties)
			{
				// Clear AL error code
				alGetError();

				// Determine type of 'Reverb' effect and apply correct settings
				ALint type;
				alGetEffecti(uiEffect, AL_EFFECT_TYPE, &type);

				// Apply selected presets to normal reverb
				if (type == AL_EFFECT_REVERB)
				{
					alEffectf(uiEffect, AL_REVERB_DENSITY, properties->flDensity);
					alEffectf(uiEffect, AL_REVERB_DIFFUSION, properties->flDiffusion);
					alEffectf(uiEffect, AL_REVERB_GAIN, properties->flGain);
					alEffectf(uiEffect, AL_REVERB_GAINHF, properties->flGainHF);
					alEffectf(uiEffect, AL_REVERB_DECAY_TIME, properties->flDecayTime);
					alEffectf(uiEffect, AL_REVERB_DECAY_HFRATIO, properties->flDecayHFRatio);
					alEffectf(uiEffect, AL_REVERB_REFLECTIONS_GAIN, properties->flReflectionsGain);
					alEffectf(uiEffect, AL_REVERB_REFLECTIONS_DELAY, properties->flReflectionsDelay);
					alEffectf(uiEffect, AL_REVERB_LATE_REVERB_GAIN, properties->flLateReverbGain);
					alEffectf(uiEffect, AL_REVERB_LATE_REVERB_DELAY, properties->flLateReverbDelay);
					alEffectf(uiEffect, AL_REVERB_AIR_ABSORPTION_GAINHF, properties->flAirAbsorptionGainHF);
					alEffectf(uiEffect, AL_REVERB_ROOM_ROLLOFF_FACTOR, properties->flRoomRolloffFactor);
					alEffecti(uiEffect, AL_REVERB_DECAY_HFLIMIT, properties->iDecayHFLimit);
				}
				// Apply full EAX reverb settings
				else
				{
					alEffectf(uiEffect, AL_EAXREVERB_DENSITY, properties->flDensity);
					alEffectf(uiEffect, AL_EAXREVERB_DIFFUSION, properties->flDiffusion);
					alEffectf(uiEffect, AL_EAXREVERB_GAIN, properties->flGain);
					alEffectf(uiEffect, AL_EAXREVERB_GAINHF, properties->flGainHF);
					alEffectf(uiEffect, AL_EAXREVERB_GAINLF, properties->flGainLF);
					alEffectf(uiEffect, AL_EAXREVERB_DECAY_TIME, properties->flDecayTime);
					alEffectf(uiEffect, AL_EAXREVERB_DECAY_HFRATIO, properties->flDecayHFRatio);
					alEffectf(uiEffect, AL_EAXREVERB_DECAY_LFRATIO, properties->flDecayLFRatio);
					alEffectf(uiEffect, AL_EAXREVERB_REFLECTIONS_GAIN, properties->flReflectionsGain);
					alEffectf(uiEffect, AL_EAXREVERB_REFLECTIONS_DELAY, properties->flReflectionsDelay);
					alEffectfv(uiEffect, AL_EAXREVERB_REFLECTIONS_PAN, properties->flReflectionsPan);
					alEffectf(uiEffect, AL_EAXREVERB_LATE_REVERB_GAIN, properties->flLateReverbGain);
					alEffectf(uiEffect, AL_EAXREVERB_LATE_REVERB_DELAY, properties->flLateReverbDelay);
					alEffectfv(uiEffect, AL_EAXREVERB_LATE_REVERB_PAN, properties->flLateReverbPan);
					alEffectf(uiEffect, AL_EAXREVERB_ECHO_TIME, properties->flEchoTime);
					alEffectf(uiEffect, AL_EAXREVERB_ECHO_DEPTH, properties->flEchoDepth);
					alEffectf(uiEffect, AL_EAXREVERB_MODULATION_TIME, properties->flModulationTime);
					alEffectf(uiEffect, AL_EAXREVERB_MODULATION_DEPTH, properties->flModulationDepth);
					alEffectf(uiEffect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, properties->flAirAbsorptionGainHF);
					alEffectf(uiEffect, AL_EAXREVERB_HFREFERENCE, properties->flHFReference);
					alEffectf(uiEffect, AL_EAXREVERB_LFREFERENCE, properties->flLFReference);
					alEffectf(uiEffect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, properties->flRoomRolloffFactor);
					alEffecti(uiEffect, AL_EAXREVERB_DECAY_HFLIMIT, properties->iDecayHFLimit);
				}
				if (alGetError() == AL_NO_ERROR)
					return true;
			}
			return false;
		}

		bool SoundManager::setEFXSoundProperties(const std::string& name, Ogre::Real airAbsorption, Ogre::Real roomRolloff, Ogre::Real coneOuterHF)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);
			
			SoundClip *sound = getSound(name);
			
			if (sound)
			{
				// Get sound source
				ALuint src = sound->getSource();

				if (src != AL_NONE)
				{
					alGetError();

					alSourcef(src, AL_AIR_ABSORPTION_FACTOR, airAbsorption);
					alSourcef(src, AL_ROOM_ROLLOFF_FACTOR, roomRolloff);
					alSourcef(src, AL_CONE_OUTER_GAINHF, coneOuterHF);

					if (alGetError() == AL_NO_ERROR)
					{
						return true;
					}
					else
					{
						Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXSoundProperties() - Unable to set EFX sound properties!", LML_NORMAL);
						return false;
					}
				}
				else
				{
					Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXSoundProperties() - No source attached to sound!", LML_NORMAL);
					return false;
				}
			}
			Ogre::LogManager::getSingleton().logMessage("UniverseSound::SoundManager::setEFXSoundProperties() - Unable to find specified sound! Spelling correct? Sound exists?", LML_NORMAL);
			return false;
		}

		void SoundManager::setEFXDistanceUnits(Ogre::Real units)
		{
			// Validation
			if (!isEFXSupported() || units <= 0) 
				return;

			// Set info to OpenAL
			alListenerf(AL_METERS_PER_UNIT, units);
		}

		bool SoundManager::hasSound(const std::string& name)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			// Find sound name in sound map
			SoundMap::iterator i = mSoundMap.find(name);
			// If the iterator is at the end of the list and hasn't found the sound, the sound doesn't exist (returns false)
			// Otherwise return true
			if(i == mSoundMap.end()) 
				return false; 
			return true;
		}

		void SoundManager::destroyAllSounds()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			// Destroy all queued sounds
			FileOpenList::iterator f = mLoadedSounds.begin();
			while (f != mLoadedSounds.end())
			{
				delete (*f);
				++f;
			}
			mLoadedSounds.clear();

			// Destroy all sounds
			SoundMap::iterator i = mSoundMap.begin();
			while(i != mSoundMap.end())
			{
				// Delete every sound in the sound map until no sound is left
				delete i->second;
				++i;
			}
			mSoundMap.clear();
			
			// Destroy shared buffers
			SharedBufferList::iterator b = mSharedBuffers.begin();
			while (b != mSharedBuffers.end())
			{
				if (b->second->mRefCount > 0)
					alDeleteBuffers(1, &b->second->mAudioBuffer);
				delete b->second;
				++b;
			}
			mSharedBuffers.clear();

			// Clear all queues
			mActiveSounds.clear();
			mPlayQueue.clear();
			mPauseQueue.clear();
			mPausedSounds.clear();
			mStopQueue.clear();
		}

		void SoundManager::playSound(const Ogre::String& soundName)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			SoundClip *sound = 0;

			if (sound = getSound(soundName))
			{
				sound->play();
			}
		}

		void SoundManager::pauseSound(const Ogre::String& soundName)
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			// Validate if there is any active sound we can stop
			if (mActiveSounds.empty()) 
				return;

			SoundClip *sound = 0;
			if (sound = getSound(soundName))
				sound->pause();
		}
/*
		void SoundManager::resumeSound(const Ogre::String& soundName)
		{
			boost::recursive_mutex::scoped_lock 1(mMutex);

			SoundClip *sound = 0;
			if (sound = getSound(soundName))
				sound->resume();
		}
*/
		void SoundManager::stopSound(const Ogre::String& soundName)
		{
			//boost::recursive_mutex::scoped_lock 1(mMutex);

			// Validation
			if (mActiveSounds.empty())
				return;

			SoundClip *sound = 0;
			// Stop the sound
			if (sound = getSound(soundName))
				sound->stop();
		}

		void SoundManager::pauseAllSounds()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			if (mActiveSounds.empty())
				return;

			for (ActiveSoundList::iterator iter = mActiveSounds.begin(); iter != mActiveSounds.end(); ++iter)
			{
				if ((*iter)->isPlaying())
				{
					// Pause sound
					(*iter)->pause();

					// Add to list of paused sounds so that the sounds can be resumed
					mPausedSounds.push_back((*iter));
				}
			}
		}

		void SoundManager::resumeAllSounds()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			if (mPausedSounds.empty())
				return;

			for (ActiveSoundList::iterator iter = mPausedSounds.begin(); iter != mPausedSounds.end(); ++iter)
			{
				(*iter)->play();
			}

			mPausedSounds.clear();
		}

		void SoundManager::stopAllSounds()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			if (mActiveSounds.empty())
				return;

			for (ActiveSoundList::iterator iter = mActiveSounds.begin(); iter != mActiveSounds.end(); ++iter)
				(*iter)->stop();
		}

		void SoundManager::destroySound(const Ogre::String& soundName)
		{
			if (soundName.empty()) 
				return;

			// Find sound in map
			SoundMap::iterator i = mSoundMap.find(soundName);
			if (i != mSoundMap.end())
			{
				// If created via plugin call destroyMovableObject() which will call _destroy()
				if (i->second->mSceneMgr)
					i->second->mSceneMgr->destroyMovableObject(i->second->getName(), SoundFactory::FACTORY_TYPE_NAME);
				// else call _destroy() directly
				else
					_destroySound(i->second);
			}
		}

		void SoundManager::_destroySound(SoundClip *sound)
		{
			if (!sound)
				return;

			boost::recursive_mutex::scoped_lock l(mMutex);

			ALuint source = sound->getSource();
			
			// Delete sound
			ALuint src = sound->getSource();
			if (src != AL_NONE) 
				releaseSoundSource(sound);

			_unregisterSound(sound);

			// Delete sounds memory
			delete sound;
		}

		void SoundManager::_unregisterSound(SoundClip *sound)
		{
			// Remove from inactive sound list
			if (!mInactiveSounds.empty())
			{
				// Remove all references
				for (ActiveSoundList::iterator iter = mInactiveSounds.begin(); iter != mInactiveSounds.end();)
				{
					if (sound == (*iter))
						mInactiveSounds.erase(iter);
					else
						++iter;
				}
			}

			// Remove from play queue
			if (!mPlayQueue.empty())
			{
				for (ActiveSoundList::iterator iter = mPlayQueue.begin(); iter != mPlayQueue.end();)
				{
					if (sound == (*iter))
						mPlayQueue.erase(iter);
					else
						++iter;
				}
			}

			// Remove from pause queue
			if (!mPauseQueue.empty())
			{
				for (ActiveSoundList::iterator iter = mPauseQueue.begin(); iter != mPauseQueue.end();)
				{
					if (sound == (*iter))
						mPauseQueue.erase(iter);
					else
						++iter;
				}
			}

			// Remove from stop queue
			if (!mStopQueue.empty())
			{
				for (ActiveSoundList::iterator iter = mStopQueue.begin(); iter != mStopQueue.end();)
				{
					if (sound == (*iter))
						mStopQueue.erase(iter);
					else
						++iter;
				}
			}

			// Remove from queued sound list (sounds loaded and waiting for play instruction)
			if (!mLoadedSounds.empty())
			{
				for (FileOpenList::iterator iter = mLoadedSounds.begin(); iter != mLoadedSounds.end();)
				{
					if (sound == (*iter)->mSound)
						mLoadedSounds.erase(iter);
					else
						++iter;
				}
			}

			// Remove from paused sound list (created by pauseAllSounds() )
			if (!mPausedSounds.empty())
			{
				for (ActiveSoundList::iterator iter = mPausedSounds.begin(); iter != mPausedSounds.end();)
				{
					if (sound == (*iter))
						mPausedSounds.erase(iter);
					else
						++iter;
				}
			}

			// Remove from active sound list 
			if (!mActiveSounds.empty())
			{
				for (ActiveSoundList::iterator iter = mActiveSounds.begin(); iter != mActiveSounds.end();)
				{
					if (sound == (*iter))
						mActiveSounds.erase(iter);
					else
						++iter;
				}
			}
		}

		void SoundManager::processThreadedSounds()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			if (mPlayQueue.empty() && mPauseQueue.empty() && mStopQueue.empty() && mLoadedSounds.empty())
				return;

			if (!mLoadedSounds.empty())
			{
				FileOpenList::iterator i = mLoadedSounds.begin();
				while (i != mLoadedSounds.end())
				{
					if ((*i)->mBuffer != AL_NONE)
					{
						(*i)->mSound->open((*i)->mFileName, (*i)->mBuffer);
					}
					else
					{
						(*i)->mSound->open((*i)->mFile);
					}

					// Prebuffer if requested
					if ((*i)->mPrebuffer) 
					{
						requestSoundSource((*i)->mSound);
					}
				}

				// Remove from queue
				delete (*i);
				i = mLoadedSounds.erase(i);
			}

			if (!mPlayQueue.empty())
			{
				ActiveSoundList::iterator iter = mPlayQueue.begin();
				while(iter != mPlayQueue.end())
				{
					// Perform requested action
					(*iter)->play();

					// If successful remove from list
					if ((*iter)->isPlaying())
					{
						iter = mPlayQueue.erase(iter);
					}
					else
					{
						++iter;
					}
				}
			}

			if (!mPauseQueue.empty())
			{
				ActiveSoundList::iterator iter = mPauseQueue.begin();
				while(iter != mPauseQueue.end())
				{
					// Perform requested action
					(*iter)->pause();

					// If successful remove from list
					if ((*iter)->isPaused())
					{
						iter = mPauseQueue.erase(iter);
					}
					else
					{
						++iter;
					}
				}
			}

			if (!mStopQueue.empty())
			{
				ActiveSoundList::iterator i = mStopQueue.begin();
				while( i != mStopQueue.end())
				{
					// Perform requested action
					(*i)->stop();

					// If successful remove from list
					if (!(*i)->isPlaying())
					{
						i = mStopQueue.erase(i);
					}
					else
					{
						++i;
					}
				}
			}
		}

		void SoundManager::updateBuffers()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			ActiveSoundList::iterator iter = mActiveSounds.begin();

			while (iter != mActiveSounds.end())
			{
				(*iter)->_updateAudioBuffers();
				++iter;
			}

			Sleep(2);
		}

		void SoundManager::_release()
		{
			boost::recursive_mutex::scoped_lock l(mMutex);

			SoundMap::iterator i = mSoundMap.begin();
			while(i != mSoundMap.end())
			{
				delete i->second;
				++i;
			}

			mSoundMap.clear();

			SourceList::iterator it = mSourcePool.begin();
			while (it != mSourcePool.end())
			{
				if (isEFXSupported())
				{
					// Remove filters/effects
					alSourcei(static_cast<ALuint>((*it)), AL_DIRECT_FILTER, AL_FILTER_NULL);
					alSource3i(static_cast<ALuint>((*it)), AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
				}
				alDeleteSources(1,&(*it));
				++it;
			}

			mSourcePool.clear();

			// Shared buffers
			SharedBufferList::iterator b = mSharedBuffers.begin();
			while (b != mSharedBuffers.end())
			{
				if (b->second->mRefCount>0)
				{
					alDeleteBuffers(1, &b->second->mAudioBuffer);
				}
				delete b->second;
				++b;
			}

			mSharedBuffers.clear();

			// Clear queues
			mPlayQueue.clear();
			mPauseQueue.clear();
			mStopQueue.clear();

			if ( mFilterList )
			{
				for ( EffectList::iterator iter=mFilterList->begin(); iter!=mFilterList->end(); ++iter )
					alDeleteEffects( 1, &iter->second);
				delete mFilterList;
				mFilterList=0;
			}

			if ( mEffectList )
			{
				for ( EffectList::iterator iter=mEffectList->begin(); iter!=mEffectList->end(); ++iter )
					alDeleteEffects( 1, &iter->second);
				delete mEffectList;
				mEffectList=0;
			}

			if ( mEffectSlotList )
			{
				for ( std::vector<ALuint>::iterator iter=mEffectSlotList->begin(); iter!=mEffectSlotList->end(); ++iter )
					alDeleteEffects( 1, &(*iter));
				delete mEffectSlotList;
				mEffectSlotList=0;
			}
		}

		int SoundManager::createSourcePool()
		{
			ALuint source;
			int numSources = 0;

			while(alGetError() == AL_NO_ERROR && numSources < MAX_SOURCES)
			{
				source = 0;
				alGenSources(1,&source);
				if(source != 0)
				{
					mSourcePool.push_back(source);
					numSources++;
				}
				else
				{
					alGetError();
					break;
				}
			}

			return static_cast<int>(mSourcePool.size());
		}

		struct SoundManager::_sortNearToFar
		{
			bool operator()(SoundClip* sound1, SoundClip* sound2)
			{
				Real	d1=0.f,
						d2=0.f;
				Vector3	lPos = SoundManager::getSingleton().getListener()->getPosition();

				if ( sound1->isRelativeToListener() )
					d1 = sound1->getPosition().length();
				else
					d1 = sound1->getPosition().distance(lPos);

				if ( sound2->isRelativeToListener() )
					d2 = sound2->getPosition().length();
				else
					d2 = sound2->getPosition().distance(lPos);

				// Check sort order
				if ( d1<d2 )	return true;
				if ( d1>d2 )	return false;

				// Equal - don't sort
				return false;
			}
		};

		struct SoundManager::_sortFarToNear
		{
			bool operator()(SoundClip* sound1, SoundClip* sound2)
			{
				Real	d1=0.f,
						d2=0.f;
				Vector3	lPos = SoundManager::getSingleton().getListener()->getPosition();

				if ( sound1->isRelativeToListener() )
					d1 = sound1->getPosition().length();
				else
					d1 = sound1->getPosition().distance(lPos);

				if ( sound2->isRelativeToListener() )
					d2 = sound2->getPosition().length();
				else
					d2 = sound2->getPosition().distance(lPos);

				// Check sort order
				if ( d1>d2 )	return true;
				if ( d1<d2 )	return false;

				// Equal - don't sort
				return false;
			}
		};

		void SoundManager::_reactivateQueuedSounds()
		{
			// Any sounds to re-activate?
			if (mInactiveSounds.empty()) 
				return;

			boost::recursive_mutex::scoped_lock l(mMutex);

			// Sort list by distance
			std::sort(mInactiveSounds.begin(), mInactiveSounds.end(), _sortNearToFar());

			// Get sound object from front of list
			SoundClip *sound = mInactiveSounds.front();

			// Check sound hasn't been stopped whilst in list
			if (sound->isPlaying())
			{
				// Try to request a source for sound
				if (requestSoundSource(sound))
				{
					// play sound
					sound->play();
				}
			}
			// Else - kick off list
			else
			{
				mInactiveSounds.erase(mInactiveSounds.begin());
			}
		}

		bool SoundManager::requestSoundSource(SoundClip *sound)
		{
			// Does sound need a source?
			if (!sound)
				return false;

			boost::recursive_mutex::scoped_lock l(mMutex);

			if (sound->getSource()!=AL_NONE) return true;

			ALuint src = AL_NONE;

			// If there are still sources available
			// Pop next available off list
			if ( !mSourcePool.empty() )
			{
				// Get next available source
				src = static_cast<ALuint>(mSourcePool.back());
				// Remove from available list
				mSourcePool.pop_back();
				// log message
				// Set sounds source
				sound->setSource(src);
				// Add to active list
				mActiveSounds.push_back(sound);
				// Remove from reactivate list if reactivating..
				if ( !mInactiveSounds.empty() )
				{
					for ( ActiveSoundList::iterator rIter=mInactiveSounds.begin(); rIter!=mInactiveSounds.end(); ++rIter )
					{
						if ((*rIter) == sound)
							mInactiveSounds.erase(rIter);
					}
				}
				return true;
			}
			// All sources in use
			// Re-use an active source
			// Use either a non-playing source or a lower priority source
			else
			{
				// Get iterator for list
				ActiveSoundList::iterator iter = mActiveSounds.begin();

				// Search for a stopped sound
				while ( iter!=mActiveSounds.end() )
				{
					// Find a stopped sound - reuse its source
					if ( (*iter)->isStopped() )
					{
						ALuint src = (*iter)->getSource();
						ALuint nullSrc = AL_NONE;
						// Pause sounds
						(*iter)->pause();
						// Remove source
						(*iter)->setSource(nullSrc);
						// Attach source to new sound
						sound->setSource(src);
						// Add to reactivate list
						mInactiveSounds.push_back((*iter));
						// Remove relinquished sound from active list
						mActiveSounds.erase(iter);
						// Add new sound to active list
						mActiveSounds.push_back(sound);
						// Return success
						return true;
					}
					else
						++iter;
				}

				// Check priority...
				Ogre::uint8 priority = sound->getPriority();
				iter = mActiveSounds.begin();

				// Search for a stopped sound
				while ( iter!=mActiveSounds.end() )
				{
					// Find a stopped sound - reuse its source
					if ( (*iter)->getPriority()<sound->getPriority() )
					{
						ALuint src = (*iter)->getSource();
						ALuint nullSrc = AL_NONE;
						// Pause sounds
						(*iter)->pause();
						// Remove source
						(*iter)->setSource(nullSrc);
						// Attach source to new sound
						sound->setSource(src);
						// Add to reactivate list
						mInactiveSounds.push_back((*iter));
						// Remove relinquished sound from active list
						mActiveSounds.erase(iter);
						// Add new sound to active list
						mActiveSounds.push_back(sound);
						// Return success
						return true;
					}
					else
						++iter;
				}

				// Sort by distance
				Real	d1 = 0.f,
						d2 = 0.f;

				// Sort list by distance
				std::sort(mActiveSounds.begin(), mActiveSounds.end(), _sortFarToNear());

				// Lists should be sorted:	Active-->furthest to Nearest
				//							Reactivate-->Nearest to furthest
				SoundClip* snd1 = mActiveSounds.front();

				if ( snd1->isRelativeToListener() )
					d1 = snd1->getPosition().length();
				else
					d1 = snd1->getPosition().distance(mListener->getPosition());

				if ( sound->isRelativeToListener() )
					d1 = sound->getPosition().length();
				else
					d1 = sound->getPosition().distance(mListener->getPosition());

				// Needs swapping?
				if ( d1>d2 )
				{
					ALuint src = snd1->getSource();
					ALuint nullSrc = AL_NONE;
					// Pause sounds
					snd1->pause();
					snd1->_markPlayPosition();
					// Remove source
					snd1->setSource(nullSrc);
					// Attach source to new sound
					sound->setSource(src);
					sound->_recoverPlayPosition();
					// Add to reactivate list
					mInactiveSounds.push_back(snd1);
					// Remove relinquished sound from active list
					mActiveSounds.erase(mActiveSounds.begin());
					// Add new sound to active list
					mActiveSounds.push_back(sound);
					// Return success
					return true;
				}
			}
			// Uh oh - won't be played
			return false;
		}

		bool SoundManager::releaseSoundSource(SoundClip *sound)
		{
			if (!sound) return false;

			boost::recursive_mutex::scoped_lock l(mMutex);

			if (sound->getSource()==AL_NONE) return true;

			// Get source
			ALuint src = sound->getSource();

			// Valid source?
			if(src != AL_NONE)
			{
				ALuint source=AL_NONE;

				// Detach source from sound
				sound->setSource(source);

				// Remove from actives list
				for (ActiveSoundList::iterator iter=mActiveSounds.begin(); iter != mActiveSounds.end(); ++iter)
				{
					// Find sound in actives list
					if ((*iter) == sound)
					{
						// Remove from list
						mActiveSounds.erase(iter);

						// Make source available
						mSourcePool.push_back(src);

						// All ok
						return true;
					}
				}
			}

			return false;
		}

		ALuint SoundManager::_getSharedBuffer(const Ogre::String& soundName)
		{
			if (soundName.empty()) 
				return AL_NONE;
			
			SharedBufferList::iterator f;

			if ((f = mSharedBuffers.find(soundName)) != mSharedBuffers.end())
			{
				f->second->mRefCount++;
				return f->second->mAudioBuffer;
			}
			return AL_NONE;
		}

		bool SoundManager::releaseSharedBuffer(const Ogre::String& soundName, ALuint& buffer)
		{
			if (soundName.empty())
				return false;

			SharedBufferList::iterator f;
			if ((f = mSharedBuffers.find(soundName)) != mSharedBuffers.end())
			{
				// Decrement
				f->second->mRefCount--;
				if (f->second->mRefCount == 0)
				{
					// Delete buffer object
					alDeleteBuffers(1, &f->second->mAudioBuffer);

					// Remove from list
					mSharedBuffers.erase(f);
				}
				return true;
			}
			return false;
		}

		bool SoundManager::registerSharedBuffer(const Ogre::String& soundName, ALuint& buffer)
		{
			if (soundName.empty())
				return false;

			SharedBufferList::iterator f;
			if (( f = mSharedBuffers.find(soundName) ) == mSharedBuffers.end())
			{
				// Create struct
				sharedAudioBuffer* buf = new sharedAudioBuffer;

				// Set buffer
				buf->mAudioBuffer = buffer;

				// Set ref count
				buf->mRefCount = 1;

				// Add to list
				mSharedBuffers[soundName] = buf;
			}
			return true;
		}

		void SoundManager::queueThreadedSound(SoundClip *sound, ONLOAD_ACTION action)
		{
			// Valid?
			if (!sound) 
				return;

			switch ( action )
			{
			case ONLOAD_PLAY:
				{
					// Already in list?
					for (ActiveSoundList::iterator iter = mPlayQueue.begin(); iter != mPlayQueue.end(); ++iter)
					{
						if ((*iter) == sound)
							return;
					}

					// Add to list
					mPlayQueue.push_back(sound);
				}
				break;
			case ONLOAD_PAUSE:
				{
					// Already in list?
					for (ActiveSoundList::iterator iter = mPauseQueue.begin(); iter != mPauseQueue.end(); ++iter)
					{
						if ((*iter)==sound)
							return;
					}

					// Add to list
					mPauseQueue.push_back(sound);
				}
				break;
			case ONLOAD_STOP:
				{
					// Already in list?
					for (ActiveSoundList::iterator iter = mStopQueue.begin(); iter != mStopQueue.end(); ++iter)
					{
						if ((*iter) == sound)
							return;
					}

					// Add to list
					mStopQueue.push_back(sound);
				}
				break;
			}
		}

		void SoundManager::setDistanceModel(ALenum value)
		{
			alDistanceModel(value);
		}

		void SoundManager::setSpeed(Ogre::Real speed)
		{
			alSpeedOfSound(speed);
		}

		void SoundManager::setDopplerFactor(Ogre::Real factor)
		{
			alDopplerFactor(factor);
		}

		void SoundManager::update(Ogre::Real ftime)
		{
			static Real rtime=0.f;

			ActiveSoundList::iterator iter = mActiveSounds.begin();
			while( iter != mActiveSounds.end() )
			{
				(*iter)->update(ftime);
				iter++;
			}
			// Limit re-activation
			if ( (rtime+=ftime) > 0.1 )
			{
				// try to reactivate any
				_reactivateQueuedSounds();

				// Reset timer
				rtime=0.f;
			}

			// Update listener
			mListener->update();
		}

		void SoundManager::threadUpdate()
		{
			while (!mShutDown)
			{
				SoundManager::getSingleton().updateBuffers();
				SoundManager::getSingleton().processThreadedSounds();
				boost::this_thread::sleep(boost::posix_time::millisec(10));
			}
		}

		Listener *SoundManager::getListener()
		{
			return mListener;
		}

		bool SoundManager::isXRAMSupported()
		{
			return mXRAMSupport;
		}

		bool SoundManager::isEFXSupported()
		{
			return mEFXSupport;
		}

		bool SoundManager::isEAXSupported()
		{
			return mEAXSupport;
		}
}